/*-----------------------------------------------------------------------------
    Full nim composed by demodulater mn88473 + tuner MXL603.
    History: 
    2014/04/09 by jary.
-----------------------------------------------------------------------------*/
#include <sys_config.h>
#include <retcode.h>
#include <types.h>
#include <osal/osal.h>
#include <api/libc/alloc.h>
#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <hal/hal_gpio.h>
#include <hld/hld_dev.h>
#include <hld/nim/nim_dev.h>
#include <hld/nim/nim.h>
#include <hld/nim/nim_tuner.h>      
#include <bus/i2c/i2c.h>
#include <bus/tsi/tsi.h>

#include "nim_mn88473.h"

#include "MN_I2C.h"
#include "MN_DMD_driver.h"
#include "MN_DMD_device.h"
#include "MN_DMD_common.h"
#include "MN_TCB.h"
#include "MN88473.h"

DMD_ERROR_t DMD_I2C_Write(DMD_PARAMETER_t* param, DMD_u8_t	slvadr, DMD_u8_t adr, DMD_u8_t  data);
DMD_ERROR_t DMD_I2C_Read(DMD_PARAMETER_t* param, DMD_u8_t	slvadr, DMD_u8_t adr, DMD_u8_t* data);

#define BER_TIMEOUT_VAL  5000

#define ARRAY_SIZE(x)		(sizeof(x)/sizeof(x[0]))

#define NIM_FLAG_ENABLE    0x00000100  
#define NIM_SCAN_END       0x00000001

#define LOG_STRING_BUFFER_SIZE    255
    
#define MN88473_LOG		    DMD_DEBUG_LOG
#define MN88473_LOG_I2C		mn88473_log_i2c

static UINT8 t2_only = 0;//ony search T2 Signal flag
static UINT8 t2_lite_support = 0;

void mn88473_log_i2c(DMD_PARAMETER_t* param, UINT8 err, UINT8 write, UINT8 slv_addr, UINT8 *data, int len)
{
    int i = 0;
    
    if ( !(param->output_buffer && param->fn_output_string) )
    {
        return;
    }
    
    if (write)
    {
        MN88473_LOG(param, "I2C_Write,0x%02X", (slv_addr&0xFE));
    }
    else
    {
        MN88473_LOG(param, "I2C_Read,0x%02X", (slv_addr|1));
    }
    
    for ( i=0; i<len; ++i )
    {
        MN88473_LOG(param, ",0x%02X", data[i]);
    }
    
    if (err)
    {
        MN88473_LOG(param, "\terror");
    }
    
    MN88473_LOG(param, "\r\n");
}

static INT32 nim_mn88473_get_BER_PER_log(struct nim_device *dev)
{
    DMD_PARAMETER_t *priv = (DMD_PARAMETER_t *)dev->priv;
    DMD_u32_t ber_err=0, ber_sum=0;
    DMD_u32_t per_err=0, per_sum=0;
    OSAL_ER   result = OSAL_E_FAIL;
    UINT32    flgptn;
    
    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    
    if(OSAL_E_OK==result)
    {

        DMD_BER(priv, priv->system, &ber_err, &ber_sum);
        DMD_PER(priv, priv->system, &per_err, &per_sum);
        MN88473_LOG(priv,"(ber_err, ber_sum) = (%d, %d), (per_err, per_sum) = (%d, %d)\r\n", ber_err, ber_sum, per_err, per_sum); 
        return SUCCESS;
    }
    else 
    {
        return ERR_FAILUE;
    }
}


//=============================================================================
// MN_I2C.c
//=============================================================================

DMD_ERROR_t DMD_API DMD_I2C_open(void)
{
    //TODO:	Please call I2C initialize API here
    //this function is called by DMD_API_open

    return DMD_E_OK;
}

/*! Write 1byte */
DMD_ERROR_t DMD_I2C_Write(DMD_PARAMETER_t* param, DMD_u8_t	slvadr , DMD_u8_t adr , DMD_u8_t data )
{
    /* 11/08/01 : OKAMOTO Implement I2C read / write handler. */
    UINT8 apData[2] = {0};
    UINT16 length = 0;
    DMD_ERROR_t ret = DMD_E_OK;

    osal_mutex_lock(param->i2c_mutex_id, OSAL_WAIT_FOREVER_TIME);

    apData[0] = adr;
    apData[1] = data;
    length = (UINT16)sizeof(apData);

    if(SUCCESS != i2c_write(param->tc.ext_dm_config.i2c_type_id, slvadr, apData, length))
    {
        ret = DMD_E_ERROR;
    }
    MN88473_LOG_I2C(param, ret, 1, slvadr, apData, length);

    osal_mutex_unlock(param->i2c_mutex_id);
    return ret;
}

/*! Read 1byte */
DMD_ERROR_t DMD_I2C_Read(DMD_PARAMETER_t* param, DMD_u8_t slvadr , DMD_u8_t adr , DMD_u8_t *data )
{
/* 11/08/01 : OKAMOTO Implement I2C read / write handler. */
    UINT8 apData[1] = {0};
    UINT16 length = 1;
    DMD_ERROR_t ret = DMD_E_OK;

    osal_mutex_lock(param->i2c_mutex_id, OSAL_WAIT_FOREVER_TIME);

    apData[0] = adr;

    if(SUCCESS != i2c_write(param->tc.ext_dm_config.i2c_type_id, slvadr, apData, length))
    {
        ret = DMD_E_ERROR;
    }

    MN88473_LOG_I2C(param, ret, 1, slvadr, apData, length);

    /* 11/08/05 : OKAMOTO Correct I2C read. */
    if(SUCCESS != i2c_read(param->tc.ext_dm_config.i2c_type_id, slvadr, data, 1))
    {
        ret = DMD_E_ERROR;
    }

    MN88473_LOG_I2C(param, ret, 0, slvadr, data, 1);

    osal_mutex_unlock(param->i2c_mutex_id);
    return ret;
}

/*! Write/Read any Length*/
/*====================================================*
    DMD_I2C_WriteRead
   --------------------------------------------------
    Description     I2C Write/Read any Length.
    Argument        
					<Common Prametor>
					 slvadr (Slave Addr of demod without R/W bit.)
					<Write Prametors>
					 adr (Address of demod.) ,	
					 wdata (Write data) 
					 wlen (Write length) 
					<Read Prametors>
					 rdata (Read result) 
					 rlen (Read length)
    Return Value	DMD_ERROR_t (DMD_E_OK:success, DMD_E_ERROR:error)
 *====================================================*/
DMD_ERROR_t DMD_I2C_WriteRead(DMD_PARAMETER_t* param, DMD_u8_t	slvadr , DMD_u8_t adr , DMD_u8_t* wdata , DMD_u32_t wlen , DMD_u8_t* rdata , DMD_u32_t rlen)
{
    UINT16 length = 0;
    UINT8 *data = NULL;
    UINT8 *pNewBuf = NULL;
    UINT8 buf[128] = {0}; //In order to avoid to call malloc() frequently for small size command.
    BOOL bRetVal = ERR_FAILUE;
        
    if((wlen+1)>0xFFFF)
    {
        return DMD_E_ERROR;
    }
    else
    {
        length = (UINT16)wlen + 1;
    }

    osal_mutex_lock(param->i2c_mutex_id, OSAL_WAIT_FOREVER_TIME);

    if (length > 128)
    {
        pNewBuf = (UINT8*)malloc(length);
        data = pNewBuf;
    }
    else
    {
        data = buf;
    }

    data[0] = adr;
    memcpy(&data[1], wdata, wlen);
    bRetVal = i2c_write(param->tc.ext_dm_config.i2c_type_id, slvadr, data, length);
    MN88473_LOG_I2C(param, bRetVal, 1, slvadr, data, length);

    if (pNewBuf != NULL)
    {
        free(pNewBuf);
        pNewBuf = NULL;
    }

    if(bRetVal != SUCCESS)
    {
        osal_mutex_unlock(param->i2c_mutex_id);
        return DMD_E_ERROR;
    }

    if(rlen!=0)
    {
        //Read
        UINT16 length;
        length = (UINT16)rlen;
        bRetVal = i2c_read(param->tc.ext_dm_config.i2c_type_id, slvadr, rdata, length);
        MN88473_LOG_I2C(param, bRetVal, 0, slvadr, rdata, length);
        if(SUCCESS != bRetVal)
        {
            osal_mutex_unlock(param->i2c_mutex_id);
            return DMD_E_ERROR;
        }
    }
    osal_mutex_unlock(param->i2c_mutex_id);
    return DMD_E_OK;
}

/* **************************************************** */
/*! Write&Read any length from/to Tuner via Demodulator */
/* **************************************************** */
DMD_ERROR_t DMD_TCB_WriteRead(void* nim_dev_priv, UINT8	tuner_address , UINT8* wdata , int wlen , UINT8* rdata , int rlen)
{
    //DMD_BANK_T2_: T2 demodulator address.
    //data[]: data submitted by tuner driver, they will be sent to tuner.
    DMD_u8_t d[DMD_TCB_DATA_MAX] = {0};
    int i= 0;
    DMD_ERROR_t ret = DMD_E_ERROR ;
    DMD_PARAMETER_t *param = (DMD_PARAMETER_t*)nim_dev_priv;

    if( wlen >= DMD_TCB_DATA_MAX || rlen >= DMD_TCB_DATA_MAX )
    {
        return DMD_E_ERROR;
    }

    /* Set TCB Through Mode */
    /* 11/11/14 : OKAMOTO	Update to "MN88472_Device_Driver_111028". */
    ret  = DMD_I2C_MaskWrite( param, DMD_BANK_T2_ , DMD_TCBSET , 0x7f , 0x53 );
    ret |= DMD_I2C_Write(param,  DMD_BANK_T2_ , DMD_TCBADR , 0x00 );

    if( (wlen == 0 && rlen == 0) || (wlen != 0) ) //Write
    {
        d[0] = tuner_address & 0xFE;
        for(i=0;i<wlen;i++)	
        {
            d[i+1] = wdata[i];
        }
        /* Read/Write */
        if( !rdata && rlen != 0 )
        {
            return DMD_E_ERROR;
        }
        ret |= DMD_I2C_WriteRead(param, DMD_BANK_T2_ , DMD_TCBCOM , d , wlen + 1 , rdata , rlen );
    }
    else
    {
        //Read
        if((!rdata) || (rlen == 0))
        {
            return DMD_E_ERROR;
        }

        d[0] = tuner_address | 1;
        ret |= DMD_I2C_WriteRead(param, DMD_BANK_T2_ , DMD_TCBCOM , d , 1 , rdata , 1 );
    }

    return ret;
}	


/* '11/08/05 : OKAMOTO Implement "DMD_wait". */
/*! Timer wait */
DMD_ERROR_t DMD_wait( DMD_u32_t msecond )
{
    if(msecond>0xFFFF)
    {
        return DMD_E_ERROR;
    }

    //timer_delay_msec((UINT16) msecond);
    osal_task_sleep(msecond);
    return DMD_E_OK;
}
	
DMD_ERROR_t MN88473_CMN_slave_address_control(BOOL bSet, DMD_u8_t *p_sadr)
{
    static DMD_u8_t static_sadr=0;

    if(bSet)
    {
        if(*p_sadr>1)
        {
        return DMD_E_ERROR;
        }

        static_sadr = *p_sadr;
    }
    else
    {
        *p_sadr = static_sadr;
    }
    return DMD_E_OK;
}

DMD_ERROR_t MN88473_CMN_slave_address_select(DMD_u8_t sadr)
{
	return MN88473_CMN_slave_address_control(TRUE, &sadr);
}

/* '11/08/01 : OKAMOTO	Implement I2C read / write handler. */
/*====================================================*
    MN88472_CMN_slave_address_get
   --------------------------------------------------
    Description     Select slave address.(without R/W bit)
    Argument        sys (Set TV system.)
    Return Value	DMD_u8_t(Current slave addr include R/W bit for Ali I2C address.)
 *====================================================*/
DMD_u8_t MN88473_CMN_slave_address_get(DMD_SYSTEM_t sys)
{
    DMD_u8_t sadr = 0x00;
    DMD_u8_t slave_address = 0x00;
    DMD_ERROR_t result = DMD_E_ERROR;

    switch(sys)
    {
    	case DMD_E_DVBT:
    		slave_address = 0x18;
    		break;
    	case DMD_E_DVBT2:
    		slave_address = 0x1c;
    		break;
    	case DMD_E_DVBC:
    		slave_address = 0x1a;
    		break;
    	default:
    		return DMD_E_ERROR;
    }

    result = MN88473_CMN_slave_address_control(FALSE, &sadr);

    if(result != DMD_E_OK)
    {
    	return result;
    }

    slave_address+=sadr;
    slave_address = (slave_address<<1); //Ali I2C address, make it include R/W bit.

    return slave_address;
}


/* '11/11/14 : OKAMOTO	Update to "MN88472_Device_Driver_111028". */
/*! Get System Time (ms) */
DMD_ERROR_t DMD_timer(DMD_u32_t *tim)
{
    *tim = osal_get_tick();
    return DMD_E_OK;
}

UINT32 DMD_elapsed_time(UINT32 startTime)
{
    return osal_get_tick() - startTime;
}

//=============================================================================
// MN_DMD_driver.c
//=============================================================================
static void nim_mn88473_switch_lock_led(struct nim_device *dev, BOOL On)
{
    if(((DMD_PARAMETER_t *)dev->priv)->tc.nim_lock_cb)
    {
        ((DMD_PARAMETER_t *)dev->priv)->tc.nim_lock_cb(On);
    }
}

static INT32 config_tuner(struct nim_device *dev, UINT32 freq, UINT32 bandwidth, UINT8 *ptr_lock)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    UINT32 tuner_id = priv->tuner_id;
    UINT8 lock = 0;
    UINT8 Tuner_Retry = 0;

    do
    {
        Tuner_Retry++;

        if(priv->tc.nim_Tuner_Control(tuner_id, freq, bandwidth, 0, NULL, 0)==ERR_FAILUE)      
        {
            MN88473_LOG(priv,"Config tuner failed, I2c failed!\r\n");
        }

        if(priv->tc.nim_Tuner_Status(tuner_id, &lock) != SUCCESS)
        {
            //if i2c read failure, no lock state can be report.
            lock = 0;
            MN88473_LOG(priv,"Config tuner failed, I2c failed!\r\n");
        }
        MN88473_LOG(priv,"Tuner Lock Times=0x%d,Status=0x%d\r\n",Tuner_Retry,lock);
    
        if(Tuner_Retry > 5)
        {
            break;
        }
    }while(0 == lock);
    
    if(ptr_lock != NULL)
    {
        *ptr_lock = lock;
    }

    if(Tuner_Retry > 5)
    {
        MN88473_LOG(priv,"ERROR! Tuner Lock Fail\r\n");
        return ERR_FAILUE;      
    }
    return SUCCESS;
}

static BOOL wait_for_signal_become_stable(DMD_PARAMETER_t * param)
{
    UINT8 lock = 0;
    INT32 result = ERR_FAILUE;

    do
    {
        if( DMD_scan( param ) != DMD_E_OK )  //DEM Locks the DVB-T2 signal.
        {
            break;
        }
        
        result = SUCCESS;
    }while(0);

    return result;
}

static BOOL need_to_config_tuner(struct nim_device *dev, UINT32 freq, UINT32 bandwidth)
{
    DMD_PARAMETER_t * param = (DMD_PARAMETER_t *)dev->priv;
    UINT32 tuner_id = param->tuner_id;
    UINT8 lock = 0;
    
    return ! ((param->Frequency == freq) \
            && (param->bw == bandwidth) \
            && (param->tc.nim_Tuner_Status(tuner_id, &lock) == SUCCESS));
}

static BOOL need_to_lock_DVBT_signal(struct nim_device *dev, struct NIM_Channel_Change *change_para, BOOL NeedToConfigTuner)
{
    DMD_PARAMETER_t * param = (DMD_PARAMETER_t *)dev->priv;

    if ( (change_para->usage_type == USAGE_TYPE_AUTOSCAN) \
        || (change_para->usage_type == USAGE_TYPE_CHANSCAN) \
        || (change_para->usage_type == USAGE_TYPE_AERIALTUNE) )
    {
        return TRUE;    //Auto detect signal type for Auto Scan and Channel Scan.
    }

    if ( change_para->usage_type == USAGE_TYPE_NEXT_PIPE_SCAN )
    {
        return FALSE;
    }
        
    if (change_para->t2_signal) //Do nothing for play DVB-T2 program.
    {
        return FALSE;
    }
    
    if (param->system != DMD_E_DVBT) //DEM is not be DVB-T mode now.
    {
        return TRUE;
    }

    return NeedToConfigTuner;
}

static INT32 need_to_lock_DVBT2_signal(struct nim_device *dev, struct NIM_Channel_Change *change_para, BOOL NeedToConfigTuner, BOOL *p_change_plp_only)
{
    DMD_PARAMETER_t * param = (DMD_PARAMETER_t *)dev->priv;
    
    *p_change_plp_only = FALSE;

    if ((change_para->usage_type == USAGE_TYPE_AUTOSCAN) \
        || (change_para->usage_type == USAGE_TYPE_CHANSCAN) \
        || (change_para->usage_type == USAGE_TYPE_AERIALTUNE) \
        || (change_para->usage_type == USAGE_TYPE_NEXT_PIPE_SCAN))
    {
        return TRUE;    //Auto detect signal type for Auto Scan and Channel Scan.
    }

    if ( ! change_para->t2_signal ) //Do nothing for play DVB-T program.
    {
        return FALSE;
    }
    
    *p_change_plp_only = TRUE;
    if (param->system != DMD_E_DVBT2) //DEM is not be DVB-T2 mode now.
    {
        return TRUE;
    }
    
    if (param->plp_index != change_para->plp_index) //Current selected PLP is different with the target PLP.
    {
        return TRUE;
    }
    
    if (param->plp_id != change_para->plp_id) //Current selected PLP is different with the target PLP.
    {
        return TRUE;
    }

    if (param->t2_work_profile_mode != change_para->t2_profile) //DVB-T2 profile: base or lite.
    {
        return TRUE;
    }

    return NeedToConfigTuner;
}

static INT32 try_to_lock_DVBT_signal(struct nim_device *dev, BOOL NeedToInitSystem, BOOL NeedToConfigTuner)
{
    UINT8 retry = 0;
    UINT8 lock = 0;
    INT32 result = ERR_FAILUE;
    DMD_PARAMETER_t * param = (DMD_PARAMETER_t *)dev->priv;
    
    do
    {
        MN88473_LOG(param, "###%s(NeedToInitSystem=%d, NeedToConfigTuner=%d)\r\n",
            __FUNCTION__, NeedToInitSystem, NeedToConfigTuner);
        
        if (NeedToInitSystem || (param->system != DMD_E_DVBT))
        {
            param->t2_signal = 0;
            param->system = DMD_E_DVBT;
            DMD_set_system( param );
            NeedToConfigTuner = TRUE;
        }

        if (param->system != DMD_E_DVBT)
        {
            return result;
        }
        
        if (NeedToConfigTuner)
        {
            result = config_tuner(dev, param->Frequency, param->bandwidth, &lock); //The tuner locks the frequency.
            
            if(result != SUCCESS)
            {
                break;
            }
        }

        DMD_scan( param );//20130913, do scan only, judge lock status below   //DEM Locks the DVB-T signal.
        DMD_get_info(param, DMD_E_INFO_DVBT_LOCK);

        if ( DMD_E_LOCKED == param->info[DMD_E_INFO_DVBT_LOCK] )
        {
            result = SUCCESS;
            break;
        }
        else
        {
            result = ERR_FAILUE;
            MN88473_LOG(param, "%s Error!\r\n", __FUNCTION__);
        }
    }while(0);
    
    return result;
}

static INT32 DVBT2_change_PLP(struct nim_device *dev, UINT8 plp_index, BOOL NeedToInitSystem, BOOL NeedToConfigTuner)
{
    UINT8 lock = 0;
    INT32 result = ERR_FAILUE;
    DMD_PARAMETER_t* param = (DMD_PARAMETER_t *)dev->priv;

    MN88473_LOG(param, "%s(plp_index=%d, %d, %d)\r\n",
        __FUNCTION__, plp_index, NeedToInitSystem,  NeedToConfigTuner);

    do 
    {
        if (NeedToInitSystem || param->system != DMD_E_DVBT2)
        {
            param->t2_signal = 1;
            param->plp_num = 0;
            param->system = DMD_E_DVBT2;
            DMD_set_system( param );
            NeedToConfigTuner = TRUE;
        }

        if ( param->system != DMD_E_DVBT2 )
        {
            break;
        }

        param->t2_signal = 1;
        param->plp_index = plp_index;
        
        //select PLP No
        DMD_set_info( param , DMD_E_INFO_DVBT2_SELECTED_PLP , plp_index );

        if ( NeedToConfigTuner )
        {
            result = config_tuner(dev, param->Frequency, param->bandwidth, &lock); 
            if(result != SUCCESS)
            {
                break;
            }
        }
        DMD_scan( param ); 
        
        DMD_get_info(param, DMD_E_INFO_DVBT2_LOCK);
        
        if ( DMD_E_LOCKED == param->info[DMD_E_INFO_DVBT2_LOCK] )
        {
            result = SUCCESS;
        }
        else
        {
            result = ERR_FAILUE;
            MN88473_LOG(param, "%s: fail to lock PLP.\r\n", __FUNCTION__);
        }
    }while(0);
    
    return result;
}

DMD_ERROR_t try_to_get_next_data_plp( DMD_PARAMETER_t* param )
{
    DMD_u32_t i = 0;

    if( param->system != DMD_E_DVBT2 )
    {
        return DMD_E_ERROR;
    }

    for(i=param->plp_index+1; i<param->plp_num; i++)
    {
        //select PLP No
        DMD_set_info( param , DMD_E_INFO_DVBT2_SELECTED_PLP , i );
        wait_for_signal_become_stable( param );//Waiting for DEM status becomes stabile.

        DMD_get_info( param , DMD_E_INFO_LOCK);

        if( param->info[DMD_E_INFO_LOCK] != DMD_E_LOCKED )
        {
            DMD_DEBUG_LOG(param, "%s() error: Becomes unlock. PLP index = %d, plp_num = %d.\r\n", __FUNCTION__, i, param->plp_num);
            continue;
        }

        DMD_get_info( param , DMD_E_INFO_DVBT2_DAT_PLP_TYPE );
        if( param->info[DMD_E_INFO_DVBT2_DAT_PLP_TYPE] != DMD_E_DVBT2_PLP_TYPE_COM )
        {
            DMD_get_info( param , DMD_E_INFO_DVBT2_DAT_PLP_ID );
            DMD_get_info( param , DMD_E_INFO_DVBT2_T2_SYSTEM_ID );

            param->plp_id = param->info[DMD_E_INFO_DVBT2_DAT_PLP_ID];
            param->t2_system_id = param->info[DMD_E_INFO_DVBT2_T2_SYSTEM_ID];
            param->plp_index = i;

            DMD_DEBUG_LOG(param, "%s(): plp_num = %d, locked plp_index = %d, plp_id = %d, t2_system_id = %d.\r\n",
                __FUNCTION__, param->plp_num, param->plp_index, param->plp_id, param->t2_system_id);
            return DMD_E_OK;
        }
    }

    if( param->plp_index != i ) //Tune to original selected PLP for recover current PLP status.
    {
        DMD_set_info( param , DMD_E_INFO_DVBT2_SELECTED_PLP , param->plp_index );
        wait_for_signal_become_stable( param ); //20130913, whenever change PLP, do software reset and rescan 
    }

    return DMD_E_ERROR; 
}

static INT32 try_to_lock_next_data_plp(struct nim_device *dev, BOOL NeedToInitSystem, BOOL NeedToConfigTuner)
{
    INT32 result = ERR_FAILUE;
    DMD_PARAMETER_t *param = (DMD_PARAMETER_t *)dev->priv;

    if (try_to_get_next_data_plp( param ) != DMD_E_OK)
    {
        //MN88473_LOG_I2C(param, "%s() error.\r\n", __FUNCTION__);
        return result;
    }
    result = DVBT2_change_PLP(dev, param->plp_index, FALSE, TRUE);
       
    return result;
}

static DMD_ERROR_t try_to_get_first_data_plp( DMD_PARAMETER_t* param )
{
    DMD_u32_t i = 0;
    UINT8   DataPLPNumber = 0;

    param->plp_num = 0;    //count all PLP.
    DMD_get_info( param , DMD_E_INFO_LOCK);  //DVBT2 and L1 signalling must be locked before it can get info.

    if( param->system != DMD_E_DVBT2 || param->info[DMD_E_INFO_LOCK] != DMD_E_LOCKED )
    {
        DMD_DEBUG_LOG(param, "%s() error: fail to lock at all.\r\n", __FUNCTION__);
        return DMD_E_ERROR;
    }

    //Get PLP Number
    DMD_get_info( param , DMD_E_INFO_DVBT2_NUM_PLP );
    param->plp_num = param->info[DMD_E_INFO_DVBT2_NUM_PLP]; 

    for(i=0, DataPLPNumber=0;i<param->info[DMD_E_INFO_DVBT2_NUM_PLP];i++)
    {
        //select PLP No
        DMD_set_info( param , DMD_E_INFO_DVBT2_SELECTED_PLP , i );
        //wait_for_signal_become_stable( param );//20130913, do scan only, judge lock status below  //Waiting for DEM status becomes stabile.

        DMD_get_info( param , DMD_E_INFO_LOCK);

        if( param->info[DMD_E_INFO_LOCK] != DMD_E_LOCKED )
        {
            DMD_DEBUG_LOG(param, "%s() error: Becomes unlock. PLP index = %d, plp_num = %d.\r\n", __FUNCTION__, i, param->plp_num);
            continue;
        }

        DMD_get_info( param , DMD_E_INFO_DVBT2_DAT_PLP_TYPE );

        if( param->info[DMD_E_INFO_DVBT2_DAT_PLP_TYPE] != DMD_E_DVBT2_PLP_TYPE_COM )
        {
            DMD_get_info( param , DMD_E_INFO_DVBT2_DAT_PLP_ID );
            DMD_get_info( param , DMD_E_INFO_DVBT2_T2_SYSTEM_ID );

            param->plp_id = param->info[DMD_E_INFO_DVBT2_DAT_PLP_ID];
            param->t2_system_id = param->info[DMD_E_INFO_DVBT2_T2_SYSTEM_ID];
            param->plp_index = i;

            DMD_DEBUG_LOG(param, "%s(): plp_num = %d, locked plp_index = %d, plp_id = %d, t2_system_id = %d.\r\n", 
                __FUNCTION__, param->plp_num, param->plp_index, param->plp_id, param->t2_system_id);
            return DMD_E_OK;
        }
    }

    return DMD_E_ERROR; //No data PLP.
}

static INT32 try_to_lock_DVBT2_signal(struct nim_device *dev, BOOL NeedToInitSystem, BOOL NeedToConfigTuner)
{
    INT32 result = ERR_FAILUE;
    DMD_PARAMETER_t * param = (DMD_PARAMETER_t *)dev->priv;
    
    MN88473_LOG(param, "###%s(NeedToInitSystem=%d, NeedToConfigTuner=%d)\r\n",
        __FUNCTION__, NeedToInitSystem, NeedToConfigTuner);

    result = DVBT2_change_PLP(dev, 0, NeedToInitSystem, NeedToConfigTuner); //for lock signal before it can call DMD_get_dataPLPs().
    if (result != SUCCESS)
    {
        return result;
    }
    
    if ( try_to_get_first_data_plp( param ) != DMD_E_OK )
    {
        MN88473_LOG(param, "%s() error: DMD_get_the_first_data_PLP_info().\r\n", __FUNCTION__);
        return result;
    }
    
/*
    if ( param->plp_index != 0 )  //Tune to the first data PLP if it is not be PLP 0. PLP 0 had been locked ahead already.
    {
        result = DVBT2_change_PLP(dev, param->plp_index, TRUE, TRUE);
    }
*/       
    return result;
}

static INT32 nim_mn88473_channel_change_smart(struct nim_device *dev, struct NIM_Channel_Change *change_para)
{
    UINT8 lock = 0, Tuner_Retry = 0;
    UINT8 WAIT_LOCK_TIME = 1;
    UINT8 try_time;   
    INT32 result;
    UINT32 flgptn;
    
    DMD_PARAMETER_t * param = (DMD_PARAMETER_t *)dev->priv;
    PCOFDM_TUNER_CONFIG_API config_info = &(param->tc);
    DMD_BANDWIDTH_t	bw;	
	//DMD_IF_FREQ_t	if_freq;
    BOOL change_plp_only, NeedToInitSystem = FALSE, NeedToConfigTuner = FALSE;

    if ((change_para->freq <= 40000) || (change_para->freq >= 900000))
    {
        return ERR_FAILUE;
    }

    switch(change_para->bandwidth)
    {
      case 6:           
		bw = DMD_E_BW_6MHZ;
        break;

      case 7:          
		bw = DMD_E_BW_7MHZ;
        break;

      case 8:            
		bw = DMD_E_BW_8MHZ;
        break;

      default:          
		return ERR_FAILUE;
    }

    result = osal_flag_wait(&flgptn, param->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW|OSAL_TWF_CLR, 2000); //OSAL_WAIT_FOREVER_TIME
    
    if(OSAL_E_OK != result)
    {
 		return ERR_FAILUE;
    }

    MN88473_LOG(param, "\n\n[%s]: usage_type %d, freq %d, bandwidth %d, profile %d, priority %d, t2_signal %d, plp_index %d, plp_id %d\r\n",
        __FUNCTION__,change_para->usage_type, change_para->freq,change_para->bandwidth, change_para->t2_profile,
        change_para->priority,change_para->t2_signal, change_para->plp_index, change_para->plp_id);
        
    do
    {
		osal_mutex_lock(param->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        param->autoscan_stop_flag = 0;
        param->do_not_wait_t2_signal_locked = ( change_para->usage_type == USAGE_TYPE_AERIALTUNE ? 1:0 );

        if(t2_lite_support)
        {
            param->t2_supported_profile_type = DMD_E_DVBT2_SOC_SUPPORT_BASE_AND_LITE;
        }
        else
        {
            param->t2_supported_profile_type = DMD_E_DVBT2_SOC_SUPPORT_BASE_ONLY;
        }
            
        if ( need_to_config_tuner(dev, change_para->freq, bw/*, if_freq*/) )
        {
            if ( (param->bandwidth != change_para->bandwidth) 
              || (param->t2_work_profile_mode != change_para->t2_profile))
              NeedToInitSystem = TRUE;
            
            param->Frequency = change_para->freq;
            param->bandwidth = change_para->bandwidth;
            param->bw = bw;
            param->freq = change_para->freq;
            //param->if_freq = if_freq;
            
            NeedToConfigTuner = TRUE;
        }

        if ((!t2_only) && (need_to_lock_DVBT_signal(dev, change_para, NeedToConfigTuner)))
        {
            result = try_to_lock_DVBT_signal(dev, NeedToInitSystem, NeedToConfigTuner);
            if (result == SUCCESS)
            {
                break;
            }
        }
        
        if (param->autoscan_stop_flag)
        {
            break;
        }

        if ( need_to_lock_DVBT2_signal(dev, change_para, NeedToConfigTuner, &change_plp_only) )
        {
            if (change_plp_only)
            {
                param->t2_work_profile_mode = change_para->t2_profile;
                param->user_scan_action = DMD_E_SCAN_SET_CHANNEL_PARAM;
                param->do_not_wait_t2_signal_locked = 1;
                result = DVBT2_change_PLP(dev, change_para->plp_index, NeedToInitSystem, NeedToConfigTuner);
            }
            else
            {
                if (change_para->usage_type == USAGE_TYPE_NEXT_PIPE_SCAN)
                {
                    result = try_to_lock_next_data_plp(dev, NeedToInitSystem, NeedToConfigTuner);
                }
                else
                {
                    param->user_scan_action = DMD_E_SCAN_GET_CHANNEL_INFO;
                    result = try_to_lock_DVBT2_signal(dev, NeedToInitSystem, NeedToConfigTuner);
                    change_para->plp_num = param->plp_num;
                    change_para->t2_profile = param->t2_work_profile_mode;
                }
                change_para->plp_index = param->plp_index;
                change_para->plp_id = param->plp_id;
                change_para->t2_system_id = param->t2_system_id;
            }
        }
    }while (0);
    
    change_para->t2_signal = param->t2_signal;
    osal_mutex_unlock(param->demodMode_mutex_id);
    osal_flag_set(param->flag_id,NIM_SCAN_END);
    
    return result;
}

/*****************************************************************************
* INT32 nim_mn88473_get_specinv(struct nim_device *dev, UINT8 *Inv)
*
*  Read FEC lock status
*
*Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8 *Inv
*
*Return Value: INT32
*****************************************************************************/
static INT32 nim_mn88473_get_specinv(struct nim_device *dev, UINT8 *Inv)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    *Inv = priv->info[DMD_E_INFO_DVBT2_FEF_INTERVAL];
    return SUCCESS;
}

static UINT8 mn88473_modulation_map_to_ali_modulation(DMD_SYSTEM_t system, UINT8 mn88472_modulation)
{
    //T_NODE:	UINT32 modulation : 8;	
    //2:DQPSK 4:QPSK, 16:16 QAM, 64:64 QAM //T2: (64+1):256 QAM, //0xFF:unknown

    if (system == DMD_E_DVBT)
    {
        switch (mn88472_modulation)
        {
            case DMD_E_DVBT_CONST_QPSK:
                return 4;
            case DMD_E_DVBT_CONST_16QAM:
                return 16;
            case DMD_E_DVBT_CONST_64QAM:
                return 64;
            default:
                return 0xFF;   //unknown.
        }
    }
    else
    {
        switch (mn88472_modulation)
        {
            case DMD_E_DVBT2_PLP_MOD_QPSK:
                return 4;
            case DMD_E_DVBT2_PLP_MOD_16QAM:
                return 16;
            case DMD_E_DVBT2_PLP_MOD_64QAM:
                return 64;
            case DMD_E_DVBT2_PLP_MOD_256QAM:
                return (64+1);
            default:
                return 0xFF;   //unknown.
        }
    }
}

static UINT8 mn88473_FEC_map_to_ali_FEC(DMD_SYSTEM_t system, UINT8 mn88472_FEC)
{
    //T_NODE:	UINT16 FEC_inner			: 4;
    //T: 0:1/2, 1:2/3, 2:3/4, 3:5/6, 4:7/8  //T2: 5:3/5, 6:4/5, //0xF:unknown

    if (system == DMD_E_DVBT)
    {
        switch (mn88472_FEC)
        {
            case DMD_E_DVBT_CR_1_2:
                return 0;
            case DMD_E_DVBT_CR_2_3:
                return 1;
            case DMD_E_DVBT_CR_3_4:
                return 2;
            case DMD_E_DVBT_CR_5_6:
                return 3;
            case DMD_E_DVBT_CR_7_8:
                return 4;
            default:
                return 0xF;   //unknown.
        }
    }
    else
    {
        switch (mn88472_FEC)
        {
            case DMD_E_DVBT2_CR_1_2:
                return 0;
            case DMD_E_DVBT2_CR_2_3:
                return 1;
            case DMD_E_DVBT2_CR_3_4:
                return 2;
            case DMD_E_DVBT2_CR_5_6:
                return 3;
            case DMD_E_DVBT2_CR_3_5:
                return 5;
            case DMD_E_DVBT2_CR_4_5:
                return 6;
            default:
                return 0xF;   //unknown.
        }
    }
}

static UINT8 mn88473_gi_map_to_ali_gi(DMD_SYSTEM_t system, UINT8 mn88472_gi)
{
    //T_NODE:	UINT32 guard_interval : 8; 	
    //4: 1/4, 8: 1/8, 16: 1/16, 32:1/32  //T2: 128:1/128, (19+128):19/128, 19:19/256, //0xFF:unknown
    if (system == DMD_E_DVBT)
    {
        switch (mn88472_gi)
        {
            case DMD_E_DVBT_GI_1_32:
                return 32;
            case DMD_E_DVBT_GI_1_16:
                return 16;
            case DMD_E_DVBT_GI_1_8:
                return 8;
            case DMD_E_DVBT_GI_1_4:
                return 4;
            default:
                return 0xFF;   //unknown.
        }
    }
    else
    {
        switch (mn88472_gi)
        {
            case DMD_E_DVBT2_GI_1_32:
                return 32;
            case DMD_E_DVBT2_GI_1_16:
                return 16;
            case DMD_E_DVBT2_GI_1_8:
                return 8;
            case DMD_E_DVBT2_GI_1_4:
                return 4;
            case DMD_E_DVBT2_GI_1_128:
                return 128;
            case DMD_E_DVBT2_GI_19_128:
                return (19+128);
            case DMD_E_DVBT2_GI_19_256:
                return 19;
            default:
                return 0xFF;   //unknown.
        }
    }
}

static UINT8 mn88473_fft_mode_map_to_ali_fft_mode(DMD_SYSTEM_t system, UINT8 mn88472_fft_mode)
{
    //T_NODE:	UINT32 FFT : 8;	
    //2:2k, 8:8k //T2: 4:4k, 16:16k, 32:32k, //0xFF:unknown

    if (system == DMD_E_DVBT)
    {
        switch (mn88472_fft_mode)
        {
            case DMD_E_DVBT_MODE_2K:
                return 2;
            case DMD_E_DVBT_MODE_4K:
                return 4;
            case DMD_E_DVBT_MODE_8K:
                return 8;
            default:
                return 0xFF;   //unknown.
        }
    }
    else
    {
        switch (mn88472_fft_mode)
        {
            case DMD_E_DVBT2_MODE_2K:
                return 2;
            case DMD_E_DVBT2_MODE_4K:
                return 4;
            case DMD_E_DVBT2_MODE_8K:
                return 8;
            case DMD_E_DVBT2_MODE_1K:
                return 1;
            case DMD_E_DVBT2_MODE_16K:
                return 16;
            case DMD_E_DVBT2_MODE_32K:
                return 32;
            default:
                return 0xFF;   //unknown.
        }
    }
}

/*****************************************************************************
* INT32 nim_mn88473_get_modulation(struct nim_device *dev, UINT8 *modulation)
* Description: Read sharp6158 modulation
*  Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8* modulation
*
* Return Value: INT32
    //T_NODE:	UINT32 modulation : 8;	
    //2:DQPSK 4:QPSK, 16:16 QAM, 64:64 QAM //T2: (64+1):256 QAM, //0xFF:unknown
*****************************************************************************/
static INT32 nim_mn88473_get_modulation(struct nim_device *dev, UINT8 *modulation)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    UINT32 val = 0;
    UINT32 flgptn = 0;
    OSAL_ER result = OSAL_E_FAIL;
    
    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    if(OSAL_E_OK==result)
    {
	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        if (priv->t2_signal) //DVB-T2
        { 
            DMD_get_info(priv,DMD_E_INFO_DVBT2_DAT_PLP_MOD); 
            val = priv->info[DMD_E_INFO_DVBT2_DAT_PLP_MOD];
        } 
        else //for DVB-T mode
        { 
            DMD_get_info(priv,DMD_E_INFO_DVBT_CONSTELLATION); 		
            val = priv->info[DMD_E_INFO_DVBT_CONSTELLATION];
        } 
        *modulation = mn88473_modulation_map_to_ali_modulation(priv->system, val);
                
        result = SUCCESS;
	osal_mutex_unlock(priv->demodMode_mutex_id);
    }
    else
    {
        *modulation = 0xFF;   //unknown.
    }
    MN88473_LOG(priv,"[%s]:MODULATION=%d\n",__FUNCTION__,*modulation);
    return result;
}

/*****************************************************************************
* INT32 nim_mn88473_get_fftmode(struct nim_device *dev, UINT8 *fft_mode)
* Description: Read sharp6158 fft_mode
*  Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8* fft_mode
*        
* Return Value: INT32
    //T_NODE:	UINT32 FFT : 8;	
    //2:2k, 8:8k //T2: 4:4k, 16:16k, 32:32k, //0xFF:unknown
*****************************************************************************/

static INT32 nim_mn88473_get_fftmode(struct nim_device *dev, UINT8 *fft_mode)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    UINT32 val = 0;
    UINT32 flgptn = 0;
    OSAL_ER result = OSAL_E_FAIL;
    
    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    
    if(OSAL_E_OK==result)
    {
	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        if (priv->t2_signal) //DVB-T2
        { 
            DMD_get_info(priv,DMD_E_INFO_DVBT2_MODE); 
            val = priv->info[DMD_E_INFO_DVBT2_MODE]; 		
        } 
        else //for DVB-T mode        
        { 
            DMD_get_info(priv,DMD_E_INFO_DVBT_MODE);     
            val = priv->info[DMD_E_INFO_DVBT_MODE];		
        } 		 

        *fft_mode = mn88473_fft_mode_map_to_ali_fft_mode(priv->system, val);
                
        result = SUCCESS;
	osal_mutex_unlock(priv->demodMode_mutex_id);
    }
    else
    {
        *fft_mode = 0xFF;   //unknown.
    }
    MN88473_LOG(priv,"[%s]:FFT=%d\n",__FUNCTION__,*fft_mode);
    return result;
}

/*****************************************************************************
* INT32 nim_mn88473_get_GI(struct nim_device *dev, UINT8 *guard_interval)
* Description: Read sharp6158 guard interval
*  Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT8* guard_interval
*
* Return Value: INT32
    //T_NODE:	UINT32 guard_interval : 8; 	
    //4: 1/4, 8: 1/8, 16: 1/16, 32:1/32  //T2: 128:1/128, (19+128):19/128, 19:19/256, //0xFF:unknown
*****************************************************************************/
static INT32 nim_mn88473_get_GI(struct nim_device *dev, UINT8 *guard_interval)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    UINT32 val = 0;
    UINT32 flgptn= 0;
    OSAL_ER result = OSAL_E_FAIL;
    
    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    
    if(OSAL_E_OK==result)
    {
	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        if (priv->t2_signal) //DVB-T2
        { 
            DMD_get_info(priv, DMD_E_INFO_DVBT2_GI); 
            val = priv->info[DMD_E_INFO_DVBT2_GI]; 
        } 
        else //for DVB-T mode
        { 
            DMD_get_info(priv, DMD_E_INFO_DVBT_GI); 
            val = priv->info[DMD_E_INFO_DVBT_GI];
        } 		
        *guard_interval = mn88473_gi_map_to_ali_gi(priv->system, val);
                
        result = SUCCESS;
	osal_mutex_unlock(priv->demodMode_mutex_id);
    }
    else
    {
        *guard_interval = 0xFF;   //unknown.
    }
    MN88473_LOG(priv,"[%s]:GI=%d\n",__FUNCTION__,*guard_interval);
    return result;
}

static INT32 nim_mn88473_get_BER(struct nim_device *dev, UINT32 *vbber)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    DMD_u32_t err = 0;
    DMD_u32_t sum = 0;
    UINT32 flgptn = 0;
    OSAL_ER result = OSAL_E_FAIL;
    
    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    
    if(OSAL_E_OK==result)
    {
    	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        DMD_BER(priv,priv->system,&err, &sum);                
        if((err<sum)&&(sum!=0))
        {
            *vbber =  100 - (err*100)/sum;
        }
        else
        {
            *vbber =0;
        }
                
        result = SUCCESS;
	osal_mutex_unlock(priv->demodMode_mutex_id);
    }
    else
    {
        *vbber = 0;
    }
    //MN88473_LOG(priv,"[%s]:BER=%d\n",__FUNCTION__,*vbber);
    return result;
}

static INT32 nim_mn88473_get_PER(struct nim_device *dev, UINT8 *per)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    DMD_u32_t err = 0;
    DMD_u32_t sum = 0;
    OSAL_ER result = OSAL_E_FAIL;
    UINT32 flgptn = 0;
    
    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    
    if(OSAL_E_OK==result)
    {
    	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        DMD_PER(priv, priv->system, &err, &sum);
        
        if (sum != 0)
        {
            *per =  100 - (err*100)/sum;
        }
        else
        {
            *per =  0;
        }
                
        result = SUCCESS;
	osal_mutex_unlock(priv->demodMode_mutex_id);
    }
    else
    {
        *per =  0;
    }
    MN88473_LOG(priv,"[%s]:PER=%d\n",__FUNCTION__,*per);
    return result;
}

static INT32 nim_mn88473_get_SQI(struct nim_device *dev, UINT8 *snr)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    UINT32 flgptn = 0;
    OSAL_ER result = OSAL_E_FAIL;

    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);

    if(OSAL_E_OK == result)
    {
	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        if (priv->t2_signal)
        { 
            DMD_get_info(priv,DMD_E_INFO_DVBT2_SQI); 
            *snr = priv->info[DMD_E_INFO_DVBT2_SQI]; 
        }
        else
        {
            DMD_get_info(priv,DMD_E_INFO_DVBT_SQI); 
            *snr = priv->info[DMD_E_INFO_DVBT_SQI]; 
        }
        
        result = SUCCESS;
	osal_mutex_unlock(priv->demodMode_mutex_id);
    }
    else
    {
    *snr =0;
    }	
    //MN88473_LOG(priv,"[%s]:SQI=%d\n",__FUNCTION__,*snr);

    return SUCCESS;
}

static INT32 nim_mn88473_get_SSI(struct nim_device *dev, UINT8 *ssi)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    UINT32 flgptn = 0;
    UINT8 agc_min = 0;
    UINT8 agc_max = 0;
    UINT8 i=0;
    UINT16 sum=0;
    OSAL_ER result = OSAL_E_FAIL;

    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);

    if(OSAL_E_OK == result)
    {
    	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        if (priv->t2_signal)
        { 
            *ssi =(UINT8)(Calc_T2_SSI(priv)); 			
        }
        else
        {
            *ssi=(UINT8)(Calc_T_SSI(priv)); 			
        }
        
        result = SUCCESS;
	osal_mutex_unlock(priv->demodMode_mutex_id);
    }
    else
    {
        *ssi =0;
    }	
    //MN88473_LOG(priv, "[%s]:SSI=%d\n",__FUNCTION__,*ssi);
    
    return result;
}

static INT32 nim_mn88473_get_FEC(struct nim_device *dev, UINT8* FEC)
{
    DMD_PARAMETER_t * param = (DMD_PARAMETER_t *)dev->priv;
    UINT32 val = 0;
    UINT32 flgptn = 0;
    OSAL_ER result = OSAL_E_FAIL;
    UINT8 FEC_type = 0;
    
    result = osal_flag_wait(&flgptn, param->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    
    if(OSAL_E_OK==result)
    {
    	osal_mutex_lock(param->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

    	if(param->t2_signal) //DVB-T2
    	{						  
            DMD_get_info(param, DMD_E_INFO_DVBT2_DAT_PLP_COD);
            val = param->info[DMD_E_INFO_DVBT2_DAT_PLP_COD];

            DMD_get_info(param, DMD_E_INFO_DVBT2_DAT_PLP_FEC_TYPE);
            FEC_type = param->info[DMD_E_INFO_DVBT2_DAT_PLP_FEC_TYPE];   //DMD_DVBT2_FEC_TYPE_t: LDPC_16K, LDPC_64K
    	} 
    	else //for DVB-T mode
    	{ 
            DMD_DVBT_HIERARCHY_t   hierarchy_exist;
            DMD_DVBT_HIER_SEL_t hierarchy_selection;
            DMD_u32_t DMD_id;

            // Check HP stream exist or not 	 
            DMD_get_info(param, DMD_E_INFO_DVBT_HIERARCHY);			
            hierarchy_exist= param->info[DMD_E_INFO_DVBT_HIERARCHY];

            DMD_get_info(param, DMD_E_INFO_DVBT_HIERARCHY_SELECT);			
            hierarchy_selection = param->info[DMD_E_INFO_DVBT_HIERARCHY_SELECT];

            if (hierarchy_exist != DMD_E_DVBT_HIERARCHY_NO)
            {
                if (hierarchy_selection == DMD_E_DVBT_HIER_SEL_HP)//Hierarchy, Alpha 1,2,4 & Customer chooses to decode HP data stream
                {						
                    DMD_id = DMD_E_INFO_DVBT_HP_CODERATE;
                }	
                else //Non-hierarchy OR Customer choose LP when HP&LP both transmitted. 
                {
                    DMD_id = DMD_E_INFO_DVBT_LP_CODERATE;
                }
            }
            else //no hierarchy 
            {   
                //if not open HIERARCHY, DVB-T code rate gets from HP area. 
                DMD_id = DMD_E_INFO_DVBT_HP_CODERATE;
            }		

            DMD_get_info(param, DMD_id);   //DMD_E_INFO_DVBT_LP_CODERATE
            val = param->info[DMD_id];
    	}
        *FEC = mn88473_FEC_map_to_ali_FEC(param->system, val);
                
        result = SUCCESS;
	osal_mutex_unlock(param->demodMode_mutex_id);
    }
    else
    {
        *FEC = 0xFF;
    }
    MN88473_LOG(param,"[%s]:FEC=%d\n",__FUNCTION__,*FEC);
    return result;
}

/*****************************************************************************
* INT32 nim_mn88473_get_freq(struct nim_device *dev, UINT32 *freq)
* Read M3327 frequence
*
* Arguments:
*  Parameter1: struct nim_device *dev
*  Parameter2: UINT32 *freq         :
*
* Return Value: void
*****************************************************************************/
static INT32 nim_mn88473_get_freq(struct nim_device *dev, UINT32 *freq)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    *freq = priv->Frequency;
    return SUCCESS;
}

static INT32 nim_mn88473_get_lock(struct nim_device *dev, UINT8 *lock)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    UINT8 data=0;
    OSAL_ER result = OSAL_E_FAIL;
    UINT32 flgptn = 0;
    int i = 0;

    result = osal_flag_wait(&flgptn, priv->flag_id, NIM_SCAN_END, OSAL_TWF_ANDW, 0);
    
    if(OSAL_E_OK == result)
    {
	osal_mutex_lock(priv->demodMode_mutex_id, OSAL_WAIT_FOREVER_TIME);

        DMD_get_info(priv, DMD_E_INFO_LOCK);

        if ( DMD_E_LOCKED == (priv->info[DMD_E_INFO_LOCK]) )
        {
            *lock = 1;
        }
        else
        {
            *lock = 0;
        }
    
        //put light the panel lock here. if *lock=ff, should not light on the led.
        nim_mn88473_switch_lock_led(dev, (*lock)?TRUE:FALSE);
	osal_mutex_unlock(priv->demodMode_mutex_id);

    }
    else    
    {
        *lock=0x0;
    }

    return SUCCESS;
}

INT32 nim_mn88473_ioctl(struct nim_device *dev, INT32 cmd, UINT32 param)   
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    INT32 freq_offset = 0;
    UINT8 data[2] = {0};
    INT32 ret_val = SUCCESS;

    if(cmd & NIM_TUNER_COMMAND)
    {
        if(NIM_TUNER_CHECK == cmd)
        {
            return SUCCESS;
        }

        if(priv->tc.nim_Tuner_Command != NULL)
        {
            ret_val = priv->tc.nim_Tuner_Command(priv->tuner_id, cmd, param);
        }
        else
        {
            ret_val = ERR_FAILUE;
        }
        
        return ret_val;
    }

    switch( cmd )
    {
        case NIM_DRIVER_STOP_ATUOSCAN:
            priv->autoscan_stop_flag = param;
            break;
    
        case NIM_DRIVER_DISABLE_DEBUG_LOG:
            param = (UINT32)NULL;
        case NIM_DRIVER_ENABLE_DEBUG_LOG:
            if(NULL == priv->output_buffer)
            {
                priv->output_buffer = (char *)MALLOC(LOG_STRING_BUFFER_SIZE);
                ASSERT(priv->output_buffer != NULL);
            }
            priv->fn_output_string = (LOG_STRING_FUNCTION)param;
            break;    
        case NIM_DRIVER_DISABLED:
            break;
        case NIM_DRIVER_GET_AGC:
            return nim_mn88473_get_SSI(dev, (UINT8 *)param);
            break;
        case NIM_DRIVER_GET_BER:
            return nim_mn88473_get_BER(dev, (UINT32 *)param);
            break;
        case NIM_DRIVER_GET_GUARD_INTERVAL:
            return nim_mn88473_get_GI(dev, (UINT8 *)param);
            break;
        case NIM_DRIVER_GET_FFT_MODE:
            return nim_mn88473_get_fftmode(dev, (UINT8 *)param);
            break;
        case NIM_DRIVER_GET_MODULATION:
            return nim_mn88473_get_modulation(dev, (UINT8 *)param);
            break;
		case NIM_DRIVER_GET_DEMOD_LOCK_MODE:
	            *(UINT8*)param = TRUE;
	            break;
		case NIM_DRIVER_SEARCH_T2_SIGNAL_ONLY:
	            t2_only = (UINT8)param;
	            break;
        case NIM_DRIVER_T2_LITE_ENABLE:
            t2_lite_support =(UINT8)param;
            break;
        case NIM_DRIVER_GET_SPECTRUM_INV:
            break;                 
        default:
            ret_val = ERR_FAILUE;
            break;
    }
    
    return ret_val;
}


static INT32 nim_mn88473_ioctl_ext(struct nim_device *dev, INT32 cmd, void * param_list)
{
    struct NIM_Channel_Change *change_para = NULL;
    
    switch (cmd)
    {
        case NIM_DRIVER_CHANNEL_CHANGE:
            change_para = (struct NIM_Channel_Change *)param_list;
            return nim_mn88473_channel_change_smart(dev, change_para);
            break;
        case NIM_DRIVER_GET_BER_VALUE:
            return nim_mn88473_get_BER_PER_log(dev);
            break;
        default:
            break;
    }

    return SUCCESS;
}

/*****************************************************************************
* INT32 nim_mn88473_close(struct nim_device *dev)
* Description: sharp6158 close
*
* Arguments:
*  Parameter1: struct nim_device *dev
*
* Return Value: INT32
*****************************************************************************/
static INT32 nim_mn88473_close(struct nim_device *dev)
{
    UINT8 data = 0;
    DMD_PARAMETER_t *priv = (DMD_PARAMETER_t *)dev->priv;

    // tuner power off
    nim_mn88473_ioctl(dev, NIM_TUNER_POWER_CONTROL, TRUE);
    nim_mn88473_switch_lock_led(dev, FALSE);
    
    return SUCCESS;
}

__ATTRIBUTE_REUSE_
static INT32 nim_mn88473_open(struct nim_device *dev)
{
    DMD_PARAMETER_t * priv = (DMD_PARAMETER_t *)dev->priv;
    struct COFDM_TUNER_CONFIG_API * config_info = &(priv->tc);
    UINT8 data = 0;
    UINT8 data_arr[4] = {0};
    UINT8 adc_fmt=0;
    DMD_u8_t rd = 0;

    if(DMD_open(priv) != DMD_E_OK )
    {
        MN88473_LOG(priv, "ERROR: DMD open\n" );
        return ERR_FAILUE;
    }
    DMD_init(priv);
    DMD_device_read_chipid(priv, &rd);
    if(DEMOD_TYPE != rd)
    {
        MN88473_LOG(priv, "ERROR !! Wrong match of CHIP[%d] and Defined DMD type[%d]! \n",rd,DEMOD_TYPE);
        return ERR_FAILUE;
    }
    
    DMD_set_system(priv);

    // tuner power on
    nim_mn88473_ioctl(dev, NIM_TUNER_POWER_CONTROL, FALSE);
    
    #ifdef DEBUG_USBPRINTF
    nim_mn88473_ioctl(dev,NIM_DRIVER_ENABLE_DEBUG_LOG,usb_printf);
    #endif
    
    osal_flag_set(priv->flag_id, NIM_SCAN_END);

    return SUCCESS;
}

static ID create_i2c_mutex(UINT32 i2c_type_id)
{
    struct nim_device *dev =  NULL;
    DMD_PARAMETER_t * priv = NULL;
    UINT16 dev_id = 0;
    UINT16 mutex_id = OSAL_INVALID_ID;

    while(1)
    {
        dev = (struct nim_device *)dev_get_by_id(HLD_DEV_TYPE_NIM, dev_id++);
        
        if(NULL == dev)
        {
            break;
        }
        
        priv = (DMD_PARAMETER_t *)dev->priv;
    }
    
    mutex_id = osal_mutex_create();
    return mutex_id;
}

/*****************************************************************************
* INT32  nim_m31_attach(char *name, PCOFDM_TUNER_CONFIG_API pConfig)
* Description: sharp6158 initialization
*
* Arguments:
*  none
*
* Return Value: INT32
*****************************************************************************/
__ATTRIBUTE_REUSE_ 
INT32 nim_mn88473_attach(char *name, PCOFDM_TUNER_CONFIG_API pConfig)
{
    struct nim_device *dev = NULL;
    DMD_PARAMETER_t *priv = NULL;
    DEM_WRITE_READ_TUNER ThroughMode;

    MEMSET(&ThroughMode, 0x00,sizeof(DEM_WRITE_READ_TUNER));
    
    if(NULL == pConfig)
    {
        MN88473_LOG(priv, "Tuner Configuration API structure is NULL!/n");
        return ERR_NO_DEV;
    }

    dev = (struct nim_device *)dev_alloc(name, HLD_DEV_TYPE_NIM, sizeof(struct nim_device));
    
    if(NULL == dev)
    {
        MN88473_LOG(priv, "Error: Alloc nim device error!\r\n");
        return ERR_NO_MEM;
    }

    /* Alloc structure space of private */
    priv = (DMD_PARAMETER_t*)MALLOC(sizeof(DMD_PARAMETER_t));    
    
    if(NULL == priv)
    {
        dev_free(dev);
        MN88473_LOG(priv, "Alloc nim device prive memory error!/n");
        return ERR_NO_MEM;
    }
    
    MEMSET((UINT8*)priv, 0, sizeof(DMD_PARAMETER_t));
    MEMCPY((UINT8*)&(priv->tc), (UINT8*)pConfig, sizeof(struct COFDM_TUNER_CONFIG_API));
    priv->system = DMD_E_DVBT2;
    priv->bw = DMD_E_BW_8MHZ;
    
    if (NIM_COFDM_TS_SSI == priv->tc.ts_mode)
    {
        priv->ts_out = DMD_E_TSOUT_SERIAL_VARIABLE_CLOCK;
    }
    else
    {
        priv->ts_out = DMD_E_TSOUT_PARALLEL_VARIABLE_CLOCK;
    }

    dev->priv = (void*)priv;

    dev->init = NULL;
    dev->open = nim_mn88473_open;
    dev->stop = nim_mn88473_close;
    dev->do_ioctl = nim_mn88473_ioctl;
    dev->do_ioctl_ext = nim_mn88473_ioctl_ext;
    
    dev->get_lock = nim_mn88473_get_lock;
    dev->get_freq = nim_mn88473_get_freq;
    dev->get_FEC = nim_mn88473_get_FEC;
    dev->get_SNR = nim_mn88473_get_SQI;//nim_mn88473_get_PER

    dev->disable = NULL;    //nim_sharp6158_disable;
    dev->get_AGC = nim_mn88473_get_SSI;//nim_mn88473_get_AGC;
    dev->get_BER = nim_mn88473_get_BER;

    dev->channel_change = NULL; //nim_mn88473_channel_change
    dev->channel_search = NULL; //nim_mn88473_channel_search

    dev->get_guard_interval = nim_mn88473_get_GI;
    dev->get_fftmode = nim_mn88473_get_fftmode;
    dev->get_modulation = nim_mn88473_get_modulation;
    dev->get_spectrum_inv = NULL; //nim_mn88473_get_specinv

    dev->get_HIER = NULL;    
    dev->get_freq_offset = NULL;    //nim_mn88473_get_freq_offset

    priv->flag_id = OSAL_INVALID_ID;
    priv->i2c_mutex_id = OSAL_INVALID_ID;
    priv->demodMode_mutex_id = OSAL_INVALID_ID;

    priv->flag_id = osal_flag_create(NIM_FLAG_ENABLE);
    if (priv->flag_id==OSAL_INVALID_ID)
    {
        MN88473_LOG(priv, "%s: no more flag\r\n", __FUNCTION__);
        return ERR_ID_FULL;
    }

    priv->i2c_mutex_id = create_i2c_mutex(priv->tc.ext_dm_config.i2c_type_id);
    if(priv->i2c_mutex_id == OSAL_INVALID_ID)   
    {
        MN88473_LOG(priv, "%s: no more mutex\r\n", __FUNCTION__);
        return ERR_ID_FULL;
    }

    priv->demodMode_mutex_id = osal_mutex_create();
    if(priv->demodMode_mutex_id == OSAL_INVALID_ID)
    {
        MN88473_LOG(priv, "%s: creat demodMode_mutex_id error\r\n", __FUNCTION__);
    }

    /* Add this device to queue */
    if(dev_register(dev) != SUCCESS)
    {
        MN88473_LOG(priv, "Error: Register nim device error!\r\n");
        FREE(priv);
        dev_free(dev);
        return ERR_NO_DEV;
    }

    if(priv->tc.nim_Tuner_Init != NULL)   
    {
        if(priv->tc.nim_Tuner_Init(&(priv->tuner_id), &(priv->tc.tuner_config)) != SUCCESS)
        {
            MN88473_LOG(priv, "Error: Init Tuner Failure!\r\n");
            return ERR_NO_DEV;
        }
        
        ThroughMode.nim_dev_priv = dev->priv;
        ThroughMode.Dem_Write_Read_Tuner = DMD_TCB_WriteRead;
        nim_mn88473_ioctl(dev, NIM_TUNER_SET_THROUGH_MODE, (UINT32)&ThroughMode);
    }

    return SUCCESS;
}


